Skip to content

Feature/identity provider abstraction#82

Open
dubemoyibe-star wants to merge 3 commits into
Thalos-Infrastructure:mainfrom
dubemoyibe-star:feature/identity-provider-abstraction
Open

Feature/identity provider abstraction#82
dubemoyibe-star wants to merge 3 commits into
Thalos-Infrastructure:mainfrom
dubemoyibe-star:feature/identity-provider-abstraction

Conversation

@dubemoyibe-star

Copy link
Copy Markdown

Identity Provider Abstraction Layer (KYC/KYB)

Summary

This PR introduces a provider-agnostic Identity Verification layer that lets Thalos
integrate multiple KYC and KYB providers without changing core business logic. The
abstraction standardizes verification workflows behind a single interface, makes the
active provider configurable, and keeps all vendor-specific code isolated from the rest
of the application (including the Agreement Service).

This layer is intended to be the foundation for all future compliance services inside
Thalos and is explicitly designed to avoid vendor lock-in.

Motivation

Compliance requirements demand flexibility in how identity verification is performed:

  • We may need to switch providers (pricing, coverage, reliability, regional support).
  • We may need to run multiple providers simultaneously (e.g. one for KYC, another for KYB).
  • Core services must never depend on a specific vendor's SDK or response format.

To achieve this, all providers implement a common interface, and the core services
depend only on that interface — never on a concrete vendor implementation.

Scope of Providers

The abstraction is designed to support the following integrations (via a single enum and
the pluggable factory). Only the mock provider is implemented in this PR; the others are
first-class citizens of the abstraction and can be added by implementing the interface and
registering the instance — no core code changes required.

  • Sumsub
  • Persona
  • Veriff
  • Synaps
  • Stripe Identity
  • Alloy
  • Mock (built-in, for local/dev/test)

Architecture

                        ┌─────────────────────────────┐
   HTTP (controller) ──▶│      VerificationService     │  core business logic
                        │  (depends only on interface) │  (isolated from vendors)
                        └──────────────┬──────────────┘
                                       │ getProvider()
                                       ▼
                        ┌─────────────────────────────┐
                        │  VerificationProviderFactory │  registry + config resolver
                        │  - default provider (env)    │
                        │  - per-provider credentials  │
                        └──────────────┬──────────────┘
                                       │ IVerificationProvider
              ┌────────────────────────┼────────────────────────┐
              ▼                        ▼                         ▼
        MockProvider            SumsubProvider            PersonaProvider  ...
       (implemented)              (future)                   (future)

Key principle: the core never imports a vendor. It resolves an
IVerificationProvider from the factory and calls the standardized operations.

Standardized Operations

Every provider implements the following lifecycle operations (IVerificationProvider):

Operation Interface method Required?
Create Verification Session createSession() Yes
Get Verification Status getStatus() Yes
Retrieve Verification Results getResults() Optional
Handle Verification Updates handleWebhook() Yes
Cancel Verification cancelSession() Optional
Verify webhook authenticity validateWebhookSignature() Yes
Apply runtime config applyConfig() Optional

getResults, cancelSession, and applyConfig are optional so that lightweight or
capability-limited providers (and test doubles) remain valid implementations. The service
degrades gracefully when a capability is absent (e.g. falls back to stored results, or
performs local-only cancellation).

Configuration

Provider selection is configurable through environment variables / application config.

Default provider

# One of: mock | sumsub | persona | veriff | synaps | stripe_identity | alloy
# Falls back to "mock" when unset or unknown.
VERIFICATION_PROVIDER="mock"

DEFAULT_VERIFICATION_PROVIDER is also accepted as an alias. Requests may still override
the provider per-session via the request body (provider field).

Per-provider credentials (convention-based)

Credentials are resolved from env using the convention
<PROVIDER>_API_KEY, <PROVIDER>_API_SECRET, <PROVIDER>_BASE_URL,
<PROVIDER>_WEBHOOK_SECRET. Example:

SUMSUB_API_KEY=""
SUMSUB_API_SECRET=""
SUMSUB_BASE_URL="https://api.sumsub.com"
SUMSUB_WEBHOOK_SECRET=""

See .env.example for the full set of documented variables for every supported provider.

API Endpoints

All routes are under the verification controller (JWT-protected unless noted):

Method Path Description
POST /verification/sessions Create Verification Session
GET /verification/sessions List a user's sessions
GET /verification/sessions/:id Get Verification Status
GET /verification/sessions/:id/results Retrieve Verification Results
DELETE /verification/sessions/:id Cancel Verification
POST /verification/webhooks/:provider Handle Verification Updates
GET /verification/providers List registered/supported providers

Files Changed

Core abstraction

  • src/verification/providers/verification.provider.ts

    • IVerificationProvider interface with the full lifecycle (added getResults?,
      cancelSession?, applyConfig?).
    • ProviderConfig type for credentials/timeouts.
  • src/verification/providers/provider-factory.ts

    • Central registry + resolver.
    • getDefaultProviderName() reads VERIFICATION_PROVIDER with safe fallback.
    • resolveConfig() maps env credentials by provider prefix.
    • hasProvider(), registerProvider(), getSupportedProviders().
  • src/verification/providers/mock.provider.ts

    • Implements the full interface incl. getResults, cancelSession, applyConfig.
    • Retains a test-only configure() helper for scripted responses/failures.

DTOs / types

  • src/verification/dto/verification.dto.ts
    • VerificationProvider enum aligned to the required vendors
      (MOCK, SUMSUB, PERSONA, VERIFF, SYNAPS, STRIPE_IDENTITY, ALLOY).
    • Added VerificationStatus.CANCELLED and cancelled_at on the session.
    • New normalized types: ProviderVerificationResult, ProviderCancelResponse.

Service / Controller

  • src/verification/verification.service.ts

    • Uses the configured default provider in createSession.
    • New getResults() — fetches + persists normalized results, falls back to stored data.
    • New cancelSession() — provider-side (best-effort) + local cancellation with
      terminal-state guarding.
  • src/verification/verification.controller.ts

    • Added GET /sessions/:id/results, DELETE /sessions/:id, POST /webhooks/:provider.

Config / Tests

  • .env.example

    • Documents VERIFICATION_PROVIDER and commented credential blocks for all six vendors.
  • src/verification/verification.abstraction.spec.ts (new)

    • Provider operations (create/status/results/cancel/webhook/signature/config).
    • Factory: default resolution, casing normalization, unknown fallback, unregistered
      provider error, config application, runtime registration.
    • Service: results retrieval + persistence, not-found, cancellation, terminal-state guard.

Adding a New Provider

  1. Create src/verification/providers/<vendor>.provider.ts implementing
    IVerificationProvider.
  2. Map the vendor's status/result payloads into the normalized DTO shapes
    (ProviderStatusResponse, ProviderVerificationResult).
  3. Register it (e.g. in VerificationProviderFactory.registerDefaults() or dynamically via
    registerProvider()).
  4. Add credentials to .env using the <PROVIDER>_* convention.

No changes to VerificationService, controllers, or any consuming service are needed.

Backward Compatibility

  • New interface methods are optional, so existing implementations (and the integration
    test's stub provider) remain valid without modification.
  • The existing verification.integration.spec.ts continues to pass unchanged.

Validation

  • npx tsc --noEmit — clean.
  • Full test suite — 8 suites / 77 tests passing (includes the pre-existing verification
    integration spec).
  • ESLint on the verification module — 0 errors (remaining warnings match the
    pre-existing Supabase result-destructuring pattern already used across the service).

Follow-ups / Notes

  • The database is managed via Supabase (no in-repo migrations). To persist cancellation in
    production, add the cancelled_at column and the cancelled value to the
    verification_sessions status enum in Supabase.
  • Webhook signature validation is enforced per-provider via validateWebhookSignature;
    concrete providers should implement vendor-specific HMAC verification.

Closes #71

@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

@dubemoyibe-star is attempting to deploy a commit to the ManuelJG's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Design Identity Provider Abstraction Layer

1 participant